UVM Verification Testbench Example

您所在的位置:网站首页 uvm test UVM Verification Testbench Example

UVM Verification Testbench Example

2024-07-14 23:55| 来源: 网络整理| 查看: 265

This session is a real example of how design and verification happens in the real industry. We'll go through the design specification, write a test plan that details how the design will be tested, develop a UVM testbench structure and verify the design.

Design

This is a simple pattern detector written in Verilog to identify a pattern in a stream of input values. On every clock, there is a new input to the design and when it matches the pattern '1011', the output out will be set to 1. For this purpose, the design is implemented as a state machine which moves through different stages as it progresses through pattern identification sequence.

Test Plan

The verification testbench will be developed in UVM and has the following block diagram:

The sequence generates a random stream of input values that will be passed to the driver as a uvm_sequence_itemThe driver receives the item and drives it to the DUT through a virtual interfaceThe monitor captures values on the DUT's input and output pin, creates a packet and sends to the scoreboardThe scoreboard is primarily responsible for checking the functional correctness of the design based on the input and output values it receives from the monitor.

The input stream of values has to be random for maximum efficiency. It should be able to catch the following scenarios:

011011011010101011100111011011TestbenchSequence Item // This is the base transaction object that will be used // in the environment to initiate new transactions and // capture transactions at DUT interface class Item extends uvm_sequence_item; `uvm_object_utils(Item) rand bit in; bit out; virtual function string convert2str(); return $sformatf("in=%0d, out=%0d", in, out); endfunction function new(string name = "Item"); super.new(name); endfunction constraint c1 { in dist {0:/20, 1:/80}; } endclass Sequence class gen_item_seq extends uvm_sequence; `uvm_object_utils(gen_item_seq) function new(string name="gen_item_seq"); super.new(name); endfunction rand int num; // Config total number of items to be sent constraint c1 { soft num inside {[10:50]}; } virtual task body(); for (int i = 0; i Driver // The driver is responsible for driving transactions to the DUT // All it does is to get a transaction from the mailbox if it is // available and drive it out into the DUT interface. class driver extends uvm_driver #(Item); `uvm_component_utils(driver) function new(string name = "driver", uvm_component parent=null); super.new(name, parent); endfunction virtual des_if vif; virtual function void build_phase(uvm_phase phase); super.build_phase(phase); if (!uvm_config_db#(virtual des_if)::get(this, "", "des_vif", vif)) `uvm_fatal("DRV", "Could not get vif") endfunction virtual task run_phase(uvm_phase phase); super.run_phase(phase); forever begin Item m_item; `uvm_info("DRV", $sformatf("Wait for item from sequencer"), UVM_HIGH) seq_item_port.get_next_item(m_item); drive_item(m_item); seq_item_port.item_done(); end endtask virtual task drive_item(Item m_item); @(vif.cb); vif.cb.in Monitor // The monitor has a virtual interface handle with which // it can monitor the events happening on the interface. // It sees new transactions and then captures information // into a packet and sends it to the scoreboard // using another mailbox. class monitor extends uvm_monitor; `uvm_component_utils(monitor) function new(string name="monitor", uvm_component parent=null); super.new(name, parent); endfunction uvm_analysis_port #(Item) mon_analysis_port; virtual des_if vif; virtual function void build_phase(uvm_phase phase); super.build_phase(phase); if (!uvm_config_db#(virtual des_if)::get(this, "", "des_vif", vif)) `uvm_fatal("MON", "Could not get vif") mon_analysis_port = new ("mon_analysis_port", this); endfunction virtual task run_phase(uvm_phase phase); super.run_phase(phase); // This task monitors the interface for a complete // transaction and writes into analysis port when complete forever begin @ (vif.cb); if (vif.rstn) begin Item item = Item::type_id::create("item"); item.in = vif.in; item.out = vif.cb.out; mon_analysis_port.write(item); `uvm_info("MON", $sformatf("Saw item %s", item.convert2str()), UVM_HIGH) end end endtask endclass Scoreboard // The scoreboard is responsible to check design functionality and // should track input and try to match the pattern and ensure that // the design has found the pattern as well. The scoreboard should // flag an error if the design didnt find the pattern and ensure // that "out" remains zero, and if the design found the pattern, // "out" is set to the correct value. class scoreboard extends uvm_scoreboard; `uvm_component_utils(scoreboard) function new(string name="scoreboard", uvm_component parent=null); super.new(name, parent); endfunction bit[`LENGTH-1:0] ref_pattern; bit[`LENGTH-1:0] act_pattern; bit exp_out; uvm_analysis_imp #(Item, scoreboard) m_analysis_imp; virtual function void build_phase(uvm_phase phase); super.build_phase(phase); m_analysis_imp = new("m_analysis_imp", this); if (!uvm_config_db#(bit[`LENGTH-1:0])::get(this, "*", "ref_pattern", ref_pattern)) `uvm_fatal("SCBD", "Did not get ref_pattern !") endfunction virtual function write(Item item); act_pattern = act_pattern Agent and Environment // Create an intermediate container called "agent" to hold // driver, monitor and sequencer class agent extends uvm_agent; `uvm_component_utils(agent) function new(string name="agent", uvm_component parent=null); super.new(name, parent); endfunction driver d0; // Driver handle monitor m0; // Monitor handle uvm_sequencer #(Item) s0; // Sequencer Handle virtual function void build_phase(uvm_phase phase); super.build_phase(phase); s0 = uvm_sequencer#(Item)::type_id::create("s0", this); d0 = driver::type_id::create("d0", this); m0 = monitor::type_id::create("m0", this); endfunction virtual function void connect_phase(uvm_phase phase); super.connect_phase(phase); d0.seq_item_port.connect(s0.seq_item_export); endfunction endclass // The environment is a container object simply to hold // all verification components together. This environment can // then be reused later and all components in it would be // automatically connected and available for use class env extends uvm_env; `uvm_component_utils(env) function new(string name="env", uvm_component parent=null); super.new(name, parent); endfunction agent a0; // Agent handle scoreboard sb0; // Scoreboard handle virtual function void build_phase(uvm_phase phase); super.build_phase(phase); a0 = agent::type_id::create("a0", this); sb0 = scoreboard::type_id::create("sb0", this); endfunction virtual function void connect_phase(uvm_phase phase); super.connect_phase(phase); a0.m0.mon_analysis_port.connect(sb0.m_analysis_imp); endfunction endclass Test

Since we want to be able to reuse the same verification environment to detect designs with patterns other than "1011", it has to be developed with enough flexibility to easily change the pattern. For this reason, we have chose to represent the pattern as an N-bit value where N is defined by the macro LENGTH as 4. Also, the base test sets up the environment, config_db and other parameters for the test so that derived tests simply have to provide a new pattern if required along with the total number of data items to be sent to the DUT as input stream. Number of data items to DUT is a randomized value that is part of the sequence and randomized to a different value in the derived test.

// Test class instantiates the environment and starts it. class base_test extends uvm_test; `uvm_component_utils(base_test) function new(string name = "base_test", uvm_component parent=null); super.new(name, parent); endfunction env e0; bit[`LENGTH-1:0] pattern = 4'b1011; gen_item_seq seq; virtual des_if vif; virtual function void build_phase(uvm_phase phase); super.build_phase(phase); // Create the environment e0 = env::type_id::create("e0", this); // Get virtual IF handle from top level and pass it to everything // in env level if (!uvm_config_db#(virtual des_if)::get(this, "", "des_vif", vif)) `uvm_fatal("TEST", "Did not get vif") uvm_config_db#(virtual des_if)::set(this, "e0.a0.*", "des_vif", vif); // Setup pattern queue and place into config db uvm_config_db#(bit[`LENGTH-1:0])::set(this, "*", "ref_pattern", pattern); // Create sequence and randomize it seq = gen_item_seq::type_id::create("seq"); seq.randomize(); endfunction virtual task run_phase(uvm_phase phase); phase.raise_objection(this); apply_reset(); seq.start(e0.a0.s0); #200; phase.drop_objection(this); endtask virtual task apply_reset(); vif.rstn Interface // The interface allows verification components to access DUT signals // using a virtual interface handle interface des_if (input bit clk); logic rstn; logic in; logic out; clocking cb @(posedge clk); default input #1step output #3ns; input out; output in; endclocking endinterface Testbench Top module tb; reg clk; always #10 clk =~ clk; des_if _if (clk); det_1011 u0 ( .clk(clk), .rstn(_if.rstn), .in(_if.in), .out(_if.out)); initial begin clk Design v1.0 module det_1011 ( input clk, input rstn, input in, output out ); parameter IDLE = 0, S1 = 1, S10 = 2, S101 = 3, S1011 = 4; reg [2:0] cur_state, next_state; assign out = cur_state == S1011 ? 1 : 0; always @ (posedge clk) begin if (!rstn) cur_state

When we simulate this design in our verification environment, we can see that there are many errors.

 Simulation Log ... UVM_INFO testbench.sv(149) @ 350: uvm_test_top.e0.sb0 [SCBD] in=1 out=0 ref=0b1011 act=0b111 UVM_INFO testbench.sv(149) @ 370: uvm_test_top.e0.sb0 [SCBD] in=1 out=0 ref=0b1011 act=0b1111 UVM_INFO testbench.sv(149) @ 390: uvm_test_top.e0.sb0 [SCBD] in=0 out=0 ref=0b1011 act=0b1110 UVM_INFO testbench.sv(149) @ 410: uvm_test_top.e0.sb0 [SCBD] in=1 out=0 ref=0b1011 act=0b1101 UVM_INFO testbench.sv(149) @ 430: uvm_test_top.e0.sb0 [SCBD] in=1 out=0 ref=0b1011 act=0b1011 UVM_INFO testbench.sv(166) @ 430: uvm_test_top.e0.sb0 [SCBD] Pattern found to match, next out should be 1 UVM_INFO testbench.sv(149) @ 450: uvm_test_top.e0.sb0 [SCBD] in=1 out=0 ref=0b1011 act=0b111 UVM_ERROR testbench.sv(156) @ 450: uvm_test_top.e0.sb0 [SCBD] ERROR ! out=0 exp=1 UVM_INFO testbench.sv(149) @ 470: uvm_test_top.e0.sb0 [SCBD] in=0 out=0 ref=0b1011 act=0b1110 ... ** Report counts by severity UVM_INFO : 527 UVM_WARNING : 0 UVM_ERROR : 19 UVM_FATAL : 0 ** Report counts by id [RNTST] 1 [SCBD] 542 [SEQ] 1 [TEST_DONE] 1 [UVM/RELNOTES] 1

The verification engineer debugs the error to confirm that these are design bugs and are reported back to the designer. The design debugs his design to find the root cause of errors, assumes he has fixed all errors and releases a second version of the design.

Click here to see the log for design v1.0

Design v1.1 module det_1011 ( input clk, input rstn, input in, output out ); parameter IDLE = 0, S1 = 1, S10 = 2, S101 = 3, S1011 = 4; reg [2:0] cur_state, next_state; assign out = cur_state == S1011 ? 1 : 0; always @ (posedge clk) begin if (!rstn) cur_state

The verification engineer reruns the same test using the same verification environment on the second design, and finds out that the number of errors have decreased but not zero yet.

 Simulation Log ... UVM_INFO testbench.sv(149) @ 950: uvm_test_top.e0.sb0 [SCBD] in=1 out=0 ref=0b1011 act=0b1111 UVM_INFO testbench.sv(149) @ 970: uvm_test_top.e0.sb0 [SCBD] in=0 out=0 ref=0b1011 act=0b1110 UVM_INFO testbench.sv(149) @ 990: uvm_test_top.e0.sb0 [SCBD] in=1 out=0 ref=0b1011 act=0b1101 UVM_INFO testbench.sv(149) @ 1010: uvm_test_top.e0.sb0 [SCBD] in=0 out=0 ref=0b1011 act=0b1010 UVM_INFO testbench.sv(149) @ 1030: uvm_test_top.e0.sb0 [SCBD] in=1 out=0 ref=0b1011 act=0b101 UVM_INFO testbench.sv(149) @ 1050: uvm_test_top.e0.sb0 [SCBD] in=1 out=0 ref=0b1011 act=0b1011 UVM_INFO testbench.sv(166) @ 1050: uvm_test_top.e0.sb0 [SCBD] Pattern found to match, next out should be 1 UVM_INFO testbench.sv(149) @ 1070: uvm_test_top.e0.sb0 [SCBD] in=1 out=0 ref=0b1011 act=0b111 UVM_ERROR testbench.sv(156) @ 1070: uvm_test_top.e0.sb0 [SCBD] ERROR ! out=0 exp=1 UVM_INFO testbench.sv(149) @ 1090: uvm_test_top.e0.sb0 [SCBD] in=0 out=0 ref=0b1011 act=0b1110 ... ** Report counts by severity UVM_INFO : 527 UVM_WARNING : 0 UVM_ERROR : 16 UVM_FATAL : 0 ** Report counts by id [RNTST] 1 [SCBD] 539 [SEQ] 1 [TEST_DONE] 1 [UVM/RELNOTES] 1

Another ticket is raised to the designer to debug and fix his design which still seem to have many bugs. The designer realizes some of the errors and releases another version.

Click here to see the log for design v1.1

Design v1.2 module det_1011 ( input clk, input rstn, input in, output out ); parameter IDLE = 0, S1 = 1, S10 = 2, S101 = 3, S1011 = 4; reg [2:0] cur_state, next_state; assign out = cur_state == S1011 ? 1 : 0; always @ (posedge clk) begin if (!rstn) cur_state

The verification engineer reruns the same test on the same environment and sees that although errors have reduced, it is not zero yet.

 Simulation Log ... UVM_INFO testbench.sv(149) @ 4130: uvm_test_top.e0.sb0 [SCBD] in=1 out=0 ref=0b1011 act=0b1101 UVM_INFO testbench.sv(149) @ 4150: uvm_test_top.e0.sb0 [SCBD] in=1 out=0 ref=0b1011 act=0b1011 UVM_INFO testbench.sv(166) @ 4150: uvm_test_top.e0.sb0 [SCBD] Pattern found to match, next out should be 1 UVM_INFO testbench.sv(149) @ 4170: uvm_test_top.e0.sb0 [SCBD] in=1 out=1 ref=0b1011 act=0b111 UVM_INFO testbench.sv(149) @ 4190: uvm_test_top.e0.sb0 [SCBD] in=0 out=0 ref=0b1011 act=0b1110 UVM_INFO testbench.sv(149) @ 4210: uvm_test_top.e0.sb0 [SCBD] in=1 out=0 ref=0b1011 act=0b1101 UVM_INFO testbench.sv(149) @ 4230: uvm_test_top.e0.sb0 [SCBD] in=1 out=0 ref=0b1011 act=0b1011 UVM_INFO testbench.sv(166) @ 4230: uvm_test_top.e0.sb0 [SCBD] Pattern found to match, next out should be 1 UVM_INFO testbench.sv(149) @ 4250: uvm_test_top.e0.sb0 [SCBD] in=0 out=0 ref=0b1011 act=0b110 UVM_ERROR testbench.sv(156) @ 4250: uvm_test_top.e0.sb0 [SCBD] ERROR ! out=0 exp=1 UVM_INFO testbench.sv(149) @ 4270: uvm_test_top.e0.sb0 [SCBD] in=1 out=0 ref=0b1011 act=0b1101 ... ** Report counts by severity UVM_INFO : 527 UVM_WARNING : 0 UVM_ERROR : 6 UVM_FATAL : 0 ** Report counts by id [RNTST] 1 [SCBD] 529 [SEQ] 1 [TEST_DONE] 1 [UVM/RELNOTES] 1

Lets assume another ticket is raised to the designer who debugs again, finds out that he missed to take some scenarios under consideration and fixes the design to release v1.3

Click here to see the log for design v1.2

Design v1.3 module det_1011 ( input clk, input rstn, input in, output out ); parameter IDLE = 0, S1 = 1, S10 = 2, S101 = 3, S1011 = 4; reg [2:0] cur_state, next_state; assign out = cur_state == S1011 ? 1 : 0; always @ (posedge clk) begin if (!rstn) cur_state

Rerun same test on the design and we see that number of errors have come down but not zero yet. Again the designer looks into the errors, and fixes those errors and provides v1.4

 Simulation Log ... UVM_INFO testbench.sv(149) @ 4190: uvm_test_top.e0.sb0 [SCBD] in=0 out=0 ref=0b1011 act=0b1110 UVM_INFO testbench.sv(149) @ 4210: uvm_test_top.e0.sb0 [SCBD] in=1 out=0 ref=0b1011 act=0b1101 UVM_INFO testbench.sv(149) @ 4230: uvm_test_top.e0.sb0 [SCBD] in=1 out=0 ref=0b1011 act=0b1011 UVM_INFO testbench.sv(166) @ 4230: uvm_test_top.e0.sb0 [SCBD] Pattern found to match, next out should be 1 UVM_INFO testbench.sv(149) @ 4250: uvm_test_top.e0.sb0 [SCBD] in=0 out=1 ref=0b1011 act=0b110 UVM_INFO testbench.sv(149) @ 4270: uvm_test_top.e0.sb0 [SCBD] in=1 out=0 ref=0b1011 act=0b1101 UVM_INFO testbench.sv(149) @ 4290: uvm_test_top.e0.sb0 [SCBD] in=1 out=0 ref=0b1011 act=0b1011 UVM_INFO testbench.sv(166) @ 4290: uvm_test_top.e0.sb0 [SCBD] Pattern found to match, next out should be 1 UVM_INFO testbench.sv(149) @ 4310: uvm_test_top.e0.sb0 [SCBD] in=0 out=0 ref=0b1011 act=0b110 UVM_ERROR testbench.sv(156) @ 4310: uvm_test_top.e0.sb0 [SCBD] ERROR ! out=0 exp=1 UVM_INFO testbench.sv(149) @ 4330: uvm_test_top.e0.sb0 [SCBD] in=1 out=0 ref=0b1011 act=0b1101 ... ** Report counts by severity UVM_INFO : 527 UVM_WARNING : 0 UVM_ERROR : 3 UVM_FATAL : 0 ** Report counts by id [RNTST] 1 [SCBD] 526 [SEQ] 1 [TEST_DONE] 1 [UVM/RELNOTES] 1

Click here to see the log for design v1.3

Design v1.4 module det_1011 ( input clk, input rstn, input in, output out ); parameter IDLE = 0, S1 = 1, S10 = 2, S101 = 3, S1011 = 4; reg [2:0] cur_state, next_state; assign out = cur_state == S1011 ? 1 : 0; always @ (posedge clk) begin if (!rstn) cur_state

Finally all errors have been solved and the design is now bug free for the given seed. Now the test has to pass hundreds of seeds to ensure the design is bug free. Ideally, the designer should run the test and resolve all bugs before making a design release, as this would reduce turn around time significantly.

Click here to see the log for design v1.4

 Simulation Log ** Report counts by severity UVM_INFO : 527 UVM_WARNING : 0 UVM_ERROR : 0 UVM_FATAL : 0 ** Report counts by id [RNTST] 1 [SCBD] 523 [SEQ] 1 [TEST_DONE] 1 [UVM/RELNOTES] 1


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3